﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using TheGameClient.Processes;
using TheGameClient.Webservices;

namespace TheGameClient
{
	public partial class FeatureHardwareStorage : ISystemFeature
	{
		private ObservableCollection<GatewayBlueprint> _availableBlueprints;
		private ObservableCollection<Cpu> _availableCpus;
		private ObservableCollection<MemoryType> _availableMemorys;
		private ObservableCollection<NetworkAdapter> _availableNics;
		private GatewayBlueprint _currentBlueprint;
		private ShallowGateway _currentGatewayConfig;

		private ObservableCollection<EntityBase> _currentHardware;

		private ObservableCollection<Cpu> _installedCpu;
		private ObservableCollection<MemoryType> _installedMemory;
		private ObservableCollection<NetworkAdapter> _installedNic;

		public FeatureHardwareStorage()
		{
			// Required to initialize variables
			InitializeComponent();
		}

		#region ISystemFeature Members

		public ulong InterfaceId
		{
			get { return new Guid("730F90CC-2E70-43AA-A013-103DCCC97729").ToLong(); }
		}

		public Guid ProcessId { get; private set; }

		public string ProcessName
		{
			get { return "Hardware Storage"; }
		}

		public InterfaceCategory Category
		{
			get { return InterfaceCategory.SystemFeature; }
		}

		public ulong SystemId { get; private set; }

		public void Load(ulong systemId)
		{
			SystemId = systemId;
			App.Client.GetStoredHardwareCompleted += ClientGetStoredHardwareCompleted;
			App.Client.GetStoredHardwareAsync(App.InternalSessionId);
		}

		#endregion

		private void ClientGetStoredHardwareCompleted(object sender, GetStoredHardwareCompletedEventArgs e)
		{
			App.Client.GetStoredHardwareCompleted -= ClientGetStoredHardwareCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result != null)
			{
				_availableCpus = new ObservableCollection<Cpu>(from x in e.Result.OfType<Cpu>() select x);
				lstAvailCpu.ItemsSource = _availableCpus;

				_availableMemorys = new ObservableCollection<MemoryType>(from x in e.Result.OfType<MemoryType>() select x);
				lstAvailMem.ItemsSource = _availableMemorys;

				_availableNics = new ObservableCollection<NetworkAdapter>(from x in e.Result.OfType<NetworkAdapter>() select x);
				lstAvailNic.ItemsSource = _availableNics;

				_availableBlueprints =
					new ObservableCollection<GatewayBlueprint>(from x in e.Result.OfType<GatewayBlueprint>() select x);
				cmbBlueprint.ItemsSource = _availableBlueprints;

				App.Client.GetCurrentHardwareCompleted += ClientGetCurrentHardwareCompleted;
				App.Client.GetCurrentHardwareAsync(App.InternalSessionId);
			}
		}

		private void ClientGetCurrentHardwareCompleted(object sender, GetCurrentHardwareCompletedEventArgs e)
		{
			App.Client.GetCurrentHardwareCompleted -= ClientGetCurrentHardwareCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result == null)
				return; //✈ ⚆⚆
			_currentHardware = new ObservableCollection<EntityBase>(e.Result.ToList());

			_installedCpu = new ObservableCollection<Cpu>(_currentHardware.OfType<Cpu>());
			lstCpu.ItemsSource = _installedCpu;

			_installedMemory = new ObservableCollection<MemoryType>(_currentHardware.OfType<MemoryType>());
			lstMemory.ItemsSource = _installedMemory;

			_installedNic = new ObservableCollection<NetworkAdapter>(_currentHardware.OfType<NetworkAdapter>());
			lstNic.ItemsSource = _installedNic;

			_currentBlueprint = _currentHardware.OfType<GatewayBlueprint>().FirstOrDefault();
			_availableBlueprints.Add(_currentBlueprint);

			App.Client.GetGatewayDetailsCompleted += ClientGetGatewayDetailsCompleted;
			App.Client.GetGatewayDetailsAsync(App.InternalSessionId);
		}

		private void ClientGetGatewayDetailsCompleted(object sender, GetGatewayDetailsCompletedEventArgs e)
		{
			App.Client.GetGatewayDetailsCompleted -= ClientGetGatewayDetailsCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result == null)
				return;

			_currentGatewayConfig = e.Result;

			_currentBlueprint =
				(from x in _availableBlueprints where x.Id == _currentGatewayConfig.BlueprintId select x).FirstOrDefault();
			if (_currentBlueprint == null)
				throw new Exception();

			cmbBlueprint.SelectedItem = _currentBlueprint;
			cmbBlueprint.SelectionChanged += CmbBlueprintSelectionChanged;
			RefreshHardwareConfigurator();
		}

		private void CmbBlueprintSelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if (cmbBlueprint.SelectedItem is GatewayBlueprint)
				_currentBlueprint = cmbBlueprint.SelectedItem as GatewayBlueprint;
			ClearInstalledHardware(ref _availableCpus, ref _installedCpu);
			lstAvailCpu.ItemsSource = _availableCpus;
			ClearInstalledHardware(ref _availableMemorys, ref _installedMemory);
			lstAvailMem.ItemsSource = _availableMemorys;
			ClearInstalledHardware(ref _availableNics, ref _installedNic);
			lstAvailNic.ItemsSource = _availableNics;
			RefreshHardwareConfigurator();
		}

		private void BtnAddCpuClick(object sender, RoutedEventArgs e)
		{
			if (lstAvailCpu.SelectedItem is Cpu)
			{
				Cpu mi = lstAvailCpu.SelectedItem as Cpu;
				if (mi.Modifier > _currentBlueprint.MaxAllowedCpuSpeed)
					return;

				if (_installedCpu.Count == _currentBlueprint.NumCpuSlots)
					return;

				double totalSpeed = _installedCpu.Sum(it => it.Modifier);
				if (totalSpeed + mi.Modifier > _currentBlueprint.MaxCpuSpeed)
					return;
				_availableCpus.Remove(mi);
				_installedCpu.Add(mi);
				RefreshHardwareConfigurator();
				if (_availableCpus.Count > 0)
					lstAvailCpu.SelectedIndex = 0;
			}
		}

		private void BtnRemoveCpuClick(object sender, RoutedEventArgs e)
		{
			if (lstCpu.SelectedItem is Cpu)
			{
				Cpu cpu = lstCpu.SelectedItem as Cpu;
				_installedCpu.Remove(cpu);
				_availableCpus.Add(cpu);
				RefreshHardwareConfigurator();
				if (_installedCpu.Count > 0)
					lstCpu.SelectedIndex = 0;
			}
		}

		private void BtnAddMemoryClick(object sender, RoutedEventArgs e)
		{
			if (lstAvailMem.SelectedItem is MemoryType)
			{
				MemoryType mem = lstAvailMem.SelectedItem as MemoryType;
				if (mem.Modifier > _currentBlueprint.MaxAllowedMemorySize)
					return;

				if (_installedMemory.Count == _currentBlueprint.NumMemorySlots)
					return;

				double totalMem = _installedMemory.Sum(it => it.Modifier);
				if (totalMem + mem.Modifier > _currentBlueprint.MaxMemorySize)
					return;
				_availableMemorys.Remove(mem);
				_installedMemory.Add(mem);
				RefreshHardwareConfigurator();
				if (_availableMemorys.Count > 0)
					lstAvailMem.SelectedIndex = 0;
			}
		}

		private void BtnRemoveMemoryClick(object sender, RoutedEventArgs e)
		{
			if (lstMemory.SelectedItem is MemoryType)
			{
				MemoryType mem = lstMemory.SelectedItem as MemoryType;
				_installedMemory.Remove(mem);
				_availableMemorys.Add(mem);
				RefreshHardwareConfigurator();
				if (_installedMemory.Count > 0)
					lstMemory.SelectedIndex = 0;
			}
		}

		private void BtnAddNicClick(object sender, RoutedEventArgs e)
		{
			if (lstAvailNic.SelectedItem is NetworkAdapter)
			{
				NetworkAdapter nwa = lstAvailNic.SelectedItem as NetworkAdapter;
				if (nwa.Modifier > _currentBlueprint.MaxAllowedBandwidth)
					return;
				if (_installedNic.Count == _currentBlueprint.NumNicSlots)
					return;
				double totalBandw = _installedNic.Sum(it => it.Modifier);
				if (totalBandw + nwa.Modifier > _currentBlueprint.MaxBandwidth)
					return;
				_availableNics.Remove(nwa);
				_installedNic.Add(nwa);
				RefreshHardwareConfigurator();
				if (_availableNics.Count > 0)
					lstAvailNic.SelectedIndex = 0;
			}
		}

		private void BtnRemoveNicClick(object sender, RoutedEventArgs e)
		{
			if (lstNic.SelectedItem is NetworkAdapter)
			{
				NetworkAdapter nwa = lstNic.SelectedItem as NetworkAdapter;
				_installedNic.Remove(nwa);
				_availableNics.Add(nwa);
				RefreshHardwareConfigurator();
				if (_installedNic.Count > 0)
					lstNic.SelectedIndex = 0;
			}
		}

		private void RefreshHardwareConfigurator()
		{
			string fmtText =
				@"Number of Cpu sockets: {0}
Number of Memory slots: {1}
Number of Nic slots: {2}

Max. allowed speed per Cpu: {3} GHz
Max. allowed capacity per memory: {4} GB
Max. allowed bandwidth per NIC: {5} GBit

Allowed total Cpu speed: {6} GHz
Allowed total memory: {7} GB
Allowed total bandwidth: {8} GBit";

			txtGatewayDetails.Text = string.Format(fmtText,
			                                       _currentBlueprint.NumCpuSlots, _currentBlueprint.NumMemorySlots,
			                                       _currentBlueprint.NumNicSlots,
			                                       _currentBlueprint.MaxAllowedCpuSpeed, _currentBlueprint.MaxAllowedMemorySize,
			                                       _currentBlueprint.MaxAllowedBandwidth,
			                                       _currentBlueprint.MaxCpuSpeed, _currentBlueprint.MaxMemorySize,
			                                       _currentBlueprint.MaxBandwidth);

			txtTotalCpuSpeed.Text = string.Format("Current total speed: {0} GHz", _installedCpu.Sum(it => it.Modifier));
			txtTotalMemory.Text = string.Format("Current total memory: {0} GB", _installedMemory.Sum(it => it.Modifier));
			txtTotalBandwidth.Text = string.Format("Current total bandwidth: {0} GBit", _installedNic.Sum(it => it.Modifier));
			if (_installedCpu.Count == 0 || _installedMemory.Count == 0 || _installedNic.Count == 0)
				btnConfigureGateway.IsEnabled = false;
			else
				btnConfigureGateway.IsEnabled = true;
		}

		private void ClearInstalledHardware<T>(ref ObservableCollection<T> available, ref ObservableCollection<T> installed)
		{
			List<T> tmp = available.ToList();
			tmp.AddRange(installed);
			available = new ObservableCollection<T>(tmp);
			installed.Clear();
		}

		private void BtnConfigureGatewayClick(object sender, RoutedEventArgs e)
		{
			if (
				MessageBox.Show(
					"Warning! Your gateway will be reset immediately for hardware configuration! Are you sure you want to reconfigure your hardware?",
					"Gateway reboot imminent!", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
			{
				ShallowGateway newConfig = new ShallowGateway();
				newConfig.BlueprintId = _currentBlueprint.Id;
				newConfig.CpuIds = (from x in _installedCpu select x.Id).ToList();
				newConfig.MemoryIds = (from x in _installedMemory select x.Id).ToList();
				newConfig.NicIds = (from x in _installedNic select x.Id).ToList();
				App.Client.ScheduleGatewayReconfigurationCompleted += Client_ScheduleGatewayReconfigurationCompleted;
				App.Client.ScheduleGatewayReconfigurationAsync(App.InternalSessionId, newConfig, true);
			}
		}

		private void Client_ScheduleGatewayReconfigurationCompleted(object sender,
		                                                            ScheduleGatewayReconfigurationCompletedEventArgs e)
		{
			App.Client.ScheduleGatewayReconfigurationCompleted -= Client_ScheduleGatewayReconfigurationCompleted;
		}
	}

	public class FeatureHardwareStorageUiFactory : UiFactoryBase
	{
		public override InterfaceCategory UiCategory
		{
			get { return InterfaceCategory.SystemFeature; }
		}

		public override IProcess CreateProgram()
		{
			return new FeatureHardwareStorage();
		}
	}
}